<?php

/**
 * 通联在线扫码支付接口
 * @author chenshubo
 */
namespace App\Service\VendorClient\AlinpayApi;


use App\Model\PssLogModel\MicroServiceLog;
use Service\AppService\IdService;
use App\Service\BaseService;
use App\Service\Code\Normal;
use App\Service\HttpServer\Http;
use App\Service\AppService\SystemCacheConfigService;

class AlinOnlineScanPayApiClient extends BaseService
{
    protected $flag = true;
    const account = 'alinscan_pay';
    protected $config = [];
    public function __construct() {
        
    }
    
    /**
     * 提交支付公司统一下单接口
     * @param array $param
     * @return mixed
     */
    public function OrderPay( $param = [] )
    {
        $this->config       = SystemCacheConfigService::instance()->getCacheInfoBySystemCode(self::account);
        $orderInfo      	= $param['payInfo'];
        $pay_money          = $orderInfo['pay_money'];
        $orderGoods     	= current($param['orderGoods']);        
        if( $param['pay_type'] == 2 ) {
            $notifyUrl = $this->config['scanPayNoticeUrl'];
            $frontUrl  = $this->config['scanPayReturnUrl'];
        } else {
            $notifyUrl = !empty($param['notifyUrl']) ? $param['notifyUrl'] : $this->config['notifyUrl'];
            $frontUrl  = !empty($param['frontUrl']) ? $param['frontUrl'] : $this->config['frontUrl'];
        }
		
        $order_sn = $orderInfo['order_sn'];
        $data = [
            'trxamt'        => bcmul((string)$pay_money, (string)100),
            'reqsn'         => $order_sn,
            'paytype'       => $this->config['paytype'],
            'randomstr'     => IdService::instance()->getOtherID(),
            'body'          => "在线扫码支付订单【{$order_sn}】",
            'remark'        => $orderInfo['bid'],
            'validtime'     => $this->config['validtime'],
            'notify_url'    => $notifyUrl,
            'front_url'     => $frontUrl,
        ];
        $requestParam = [
            'method'    => 'orderPay',
            'data'      => $data,
        ];
        $res = $this->executePay($requestParam);
        if( $res['code'] != Normal::SUC ) {
            return $res;
        }
        return Normal::returnCode(Normal::SUC,"获取成功",[
            'form_data' => [
                'order_id'          => (string)$orderInfo['id'],
                'order_sn'          => (string)$order_sn,
                'trade_money'       => number_format((float)$pay_money,2),
                'trade_receiver'    => $this->config['ebpName'],
                'trade_goods_name'  => "在线PC扫码支付订单：{$order_sn}",
            ],
            'form_url'  => $res['data']['payinfo']
        ]);
    }
    
    /**
     * 支付申报
     */
    public function OrderPayDeclare( $param = [] )
    {
        if( empty($param['payInfo']) ) {
            return Normal::returnCode(Normal::FAIL,"except param payInfo");
        }
    
        if( empty($param['orderInfo']) ) {
            return Normal::returnCode(Normal::FAIL,"except param orderInfo");
        }
    
        $payInfo        = $param['payInfo'];
        $orderInfo      = $param['orderInfo'];
        $config         = $this->config;
        $apply_extends  = @json_decode($orderInfo['apply_extends'],true);
        $pay_sn         = $apply_extends['paySerial'] ?? $orderInfo['pay_sn'];
        $pay_sn         = $param['payInfo']['pay_sn'] ?? $pay_sn;
        $supplier_dsn   = $orderInfo['supplier_dsn'];
        $data=[];
        $data['HEAD']=[
            'VERSION'           =>  'v5.6',
            'VISITOR_ID'        =>  'MCT',
            'MCHT_ID'           =>  $config['mcht_id'],
            'ORDER_NO'          =>  $supplier_dsn,
            'TRANS_DATETIME'    =>  date('YmdHis'),
            'CHARSET'           =>  '1',
            'SIGN_TYPE'         =>  '1',
            'SIGN_MSG'          =>  '',
        ];
        $data['BODY']=[
            'CUSTOMS_CODE'          =>  $config['customs_code'],
            'PAYMENT_CHANNEL'       =>  2,//支付渠道
            'CUS_ID'                =>  $config['cusid'],//验证报关商户号$this->config['cusid']
            'PAYMENT_DATETIME'      =>  date('YmdHis'),
            'MCHT_ORDER_NO'         =>  $supplier_dsn,//商户订单号
            'PAYMENT_ORDER_NO'      =>  $pay_sn,//支付流水号
            'PAYMENT_AMOUNT'        =>  bcmul((string)$orderInfo['real_total_money'], '100'),//支付总额,
            'CURRENCY'              =>  '156',
            'ESHOP_ENT_CODE'        =>  $config['ebpCode'],//电商平台代码
            'ESHOP_ENT_NAME'        =>  $config['ebpName'],//电商平台名称
            'PAYER_NAME'            =>  $orderInfo['receive_name'],//支付人姓名
            'PAPER_TYPE'            =>  '01',//支付人证件类型01身份证
            'PAPER_NUMBER'          =>  $orderInfo['buyer_idcard'],//支付人证件号码
            'PAPER_PHONE'           =>  $orderInfo['recveive_phone'],//支付人手机号
        ];
        $xml    =   '<BODY>'.$this->arr_to_xml($data['BODY'],false).'</BODY>';
        $sign   =   md5($xml."<key>{$config['mcht_key']}</key>");
        $data['HEAD']['SIGN_MSG']=strtoupper($sign);
        $msg    = '<PAYMENT_INFO>'.$this->arr_to_xml($data,false).'</PAYMENT_INFO>';
        $parameterValue = base64_encode($msg);
        $requestParam = [
            'method'    => 'reportApply',
            'data'      => $parameterValue,
        ];
        return $this->executeReport($requestParam);
    }
    
    /**
     * 重新推送报文接口
     */
    public function reportRePush( $param = [] )
    {
        $config = $this->config;
        $data=[];
        $data['HEAD']=[
            'VERSION'           =>  'v5.3',
            'TRANS_DATETIME'    =>  date('YmdHis'),
            'CHARSET'           =>  '1',
            'SIGN_TYPE'         =>  '1',
            'SIGN_MSG'          =>  '',
        ];
        $data['BODY']=[
            'VISITOR_ID'        => 'MCT',
            'CUSTOMS_CODE'      => $config['customs_code'],
            'PAYMENT_MCHT_ID'   => $config['mcht_id'],
            'PAYMENT_ORDER_NO'  => $param['pay_sn'],
            'MCHT_ORDER_NO'     => $param['supplier_dsn'],
            'BIZ_TYPE'          => ''
        ];
        $xml    =   '<BODY>'.$this->arr_to_xml($data['BODY'],false).'</BODY>';
        $sign   =   md5($xml."<key>{$config['mcht_key']}</key>");
        $data['HEAD']['SIGN_MSG']=strtoupper($sign);
        $msg    = '<PAYMENT_INFO>'.$this->arr_to_xml($data,false).'</PAYMENT_INFO>';
        $parameterValue = base64_encode($msg);
        $requestParam = [
            'method'    => 'reportRePush',
            'data'      => $parameterValue,
        ];
        return $this->executeReport($requestParam);
    }
    
    /**
     * 查询海关支付单申报情况
     */
    public function reportQuery( $param = [] )
    {
        $config = $this->config;
        $data=[];
        $data['HEAD']=[
            'VERSION'           =>  'v5.6',
            'TRANS_DATETIME'    =>  date('YmdHis'),
            'CHARSET'           =>  '1',
            'SIGN_TYPE'         =>  '1',
            'SIGN_MSG'          =>  '',
        ];
        $data['BODY']=[
            'VISITOR_ID'        => 'MCT',
            'CUSTOMS_CODE'      => $config['customs_code'],
            'PAYMENT_MCHT_ID'   => $config['mcht_id'],
            'PAYMENT_ORDER_NO'  => $param['pay_sn'],
            'MCHT_ORDER_NO'     => $param['supplier_dsn'],
        ];
        $xml    =   '<BODY>'.$this->arr_to_xml($data['BODY'],false).'</BODY>';
        $sign   =   md5($xml."<key>{$config['mcht_key']}</key>");
        $data['HEAD']['SIGN_MSG']=strtoupper($sign);
        $msg    = '<PAYMENT_INFO>'.$this->arr_to_xml($data,false).'</PAYMENT_INFO>';
        $parameterValue = base64_encode($msg);
        $requestParam = [
            'method'    => 'reportRePush',
            'data'      => $parameterValue,
        ];
        return $this->executeReport($requestParam);
    }
        
    /**
     * 订单取消接口
     * 只能撤销当天的交易，全额退款，实时返回退款结果
     * @param array $params
     * @return mixed
     */
    public function OrderCancel( $param = [] )
    {
        if( empty($param['trxamt']) ) {
            return Normal::returnCode(Normal::FAIL,"trxamt订单金额不能为空");
        }
        
        if( empty($param['oldreqsn']) ) {
            return Normal::returnCode(Normal::FAIL,"oldreqsn订单号不能为空");
        }
        
        $alinPayData = [
            'trxamt'        => bcmul($param['trxamt'], 100),//单位为分
            'reqsn'         => IdService::instance()->getOtherID(),//商户唯一订单
            'oldreqsn'      => $param['oldreqsn'],//原交易的商户交易单号
        ];
        $requestParam = [
            'method'    => 'cancelOrder',
            'data'      => $alinPayData,
        ];
        return $this->executePay($requestParam);
    }
    
    /**
     * 订单退款接口
     * 支持部分金额退款
     * @param array $params
     * @return mixed
     */
    public function OrderRefund( $param = [] )
    {
        if( empty($param['money']) ) {
            return Normal::returnCode(Normal::FAIL,"money订单金额不能为空");
        }
        
        if( empty($param['order_sn']) ) {
            return Normal::returnCode(Normal::FAIL,"order_sn订单号不能为空");
        }
        
        $alinPayData = [
            'trxamt'        => bcmul($param['money'], 100),//单位为分
            'reqsn'         => IdService::instance()->getOtherID(),//商户唯一订单
            'oldreqsn'      => $param['order_sn'],//原交易的商户交易单号
            'remark'        => '线上退款',//备注信息
        ];
        $requestParam = [
            'method'    => 'refundOrder',
            'data'      => $alinPayData,
        ];
        return $this->executePay($requestParam);
    }
    
    /**
     * 交易查询
     * @param array $params
     * @return mixed
     */
    public function OrderQuery( $param = [] )
    {
        if( empty($param['pay_sn']) ) {
            return Normal::returnCode(Normal::FAIL,"pay_sn支付流水号不能为空");
        }
        $alinPayData = [
            'trxid' => $param['pay_sn'],//支付的收银宝平台流水
        ];
        $requestParam = [
            'method'    => 'queryOrder',
            'data'      => $alinPayData,
        ];
        return $this->executePay($requestParam);
    }
    
    /**
     * 验证回调签名
     */
    public function checkSign( $param = [] )
    {
        $sign = $param['sign'];
        unset($param['sign']);
        ksort($param);
        $bufSignSrc = $this->ToUrlParams($param);
        $public_key = $this->config['public_key'];
        $public_key = chunk_split($public_key , 64, "\n");
        $key = "-----BEGIN PUBLIC KEY-----\n$public_key-----END PUBLIC KEY-----\n";
        $result= openssl_verify($bufSignSrc,base64_decode($sign), $key );
        return $result;
    }
    
    /**
     * 获取签名参数
     * @param array $param
     */
    protected function getSign( $param = [] )
    {
        $parameter = array_merge($param,$this->getCommonParameters());
        ksort($parameter);
        //组装签名字符串
        $bufSignSrc = self::ToUrlParams($parameter);
        //进行RSA签名
        $private_key = chunk_split($this->config['private_key'] , 64, "\n");
        $key = "-----BEGIN RSA PRIVATE KEY-----\n".wordwrap($private_key)."-----END RSA PRIVATE KEY-----";
        $signRes = openssl_sign($bufSignSrc, $signature, $key );
        if( !$signRes ) {
            return Normal::returnCode(Normal::FAIL,"签名失败");
        }
        
        //加密后的内容通常含有特殊字符，需要编码转换下，在网络间通过url传输时要注意base64编码是否是url安全的
        $parameter['sign'] = base64_encode($signature);
        return Normal::returnCode(Normal::SUC,"签名成功",$parameter);
    }
    
    /**
     * 处理参数签名
     * @param array $array
     * @return string
     */
    public static function ToUrlParams(array $array)
    {
        $buff = "";
        foreach ($array as $k => $v)
        {
            if($v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }
        
        $buff = trim($buff, "&");
        return $buff;
    }
    
    /**
     * 获取公共参数配置
     */
    protected function getCommonParameters()
    {
        $param = [
            'cusid'         => $this->config['cusid'],//实际交易商户号
            'appid'         => $this->config['appid'],//平台分配的APPID
            'version'       => $this->config['version'],
            'signtype'      => $this->config['signtype'],//签名类型
            'randomstr'     => md5(microtime(true) . mt_rand(1000,9999)),
        ];
        return $param;
    }
    
    public function xmlToArray($xml)
    {
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }
    
    public function arr_to_xml($data, $root = true)
    {
        $str = "";
        if ($root) {
            $str .= "<xml>";
        }
        foreach ($data as $key => $val) {
            $key= trim($key);
    
            if (is_array($val)) {
                $child = $this->arr_to_xml($val,false);
                $str.="<$key>$child</$key>";
            } else {
                $val= trim($val);
                $str.="<$key>$val</$key>";
            }
        }
        if ($root) {
            $str .= "</xml>";
        }
        return str_replace(PHP_EOL,"",$str);
    }
    
    /**
     * CURL报关相关请求
     * @param $params
     * @return mixed
     */
    public function executeReport( $param = [] )
    {
        $method     = $param['method'];
        $data       = $param['data'];
        $method     = $param['method'];
        $url        = $this->config[$method];
        $start_time = microtime(true);
        $res        = Http::apiRequest($url,['data' => $data],'post',["content-type: application/x-www-form-urlencoded;charset=UTF-8"]);
        $result     = $this->xmlToArray(base64_decode($res));
        $returnMsg  = [];
        if( isset($result['BODY']) && isset($result['BODY']['RETURN_CODE'])) {
            if( $result['BODY']['RETURN_CODE'] == '0000' ) {
                $returnMsg = Normal::returnCode(Normal::SUC,"提交支付单申报成功",$result);
            } else {
                $returnMsg = Normal::returnCode(Normal::FAIL,$result['BODY']['RETURN_MSG']);
            }
        } else {
            $returnMsg = Normal::returnCode(Normal::FAIL,"请求失败");
        }
        MicroServiceLog::instance()->addLog([
            'interface_code'    => self::account.'_'.$method,
            'request_url'       => $url,
            'request_param'     => $this->xmlToArray(base64_decode($data)),
            'response_code'     => $returnMsg['code'],
            'response_data'     => $res,
            'start_time'        => $start_time,
        ]);
        return $returnMsg;
    }
    
    /**
     * CURL请求
     * @param $params
     * @return mixed
     */
    public function executePay( $param = [] )  
    {
        $method     = $param['method'];
        $data       = $param['data'];
        $method     = $param['method'];
        $url        = $this->config[$method];
        $res = $this->getSign($data);
        if( $res['code'] != Normal::SUC ) {
            return $res;
        }
        $data       = $res['data'];
        $start_time = microtime(true);
        $res        = Http::apiRequest($url,$data,'post',["content-type: application/x-www-form-urlencoded;charset=UTF-8"]);
        $response   = json_decode($res,true);
        $returnMsg  = [];
        if( isset($response['retcode']) && $response['retcode'] == 'SUCCESS' ) {
            if( $response['trxstatus'] == '0000' ) {
                
                $returnMsg = Normal::returnCode(Normal::SUC,"交易成功",$response);
                
            } else {
                $returnMsg = Normal::returnCode(Normal::FAIL,$response['errmsg']);
            }
        } else if( isset($response['retcode']) ) {
            $returnMsg = Normal::returnCode(Normal::FAIL,$response['retmsg']);
        } else {
            $returnMsg = Normal::returnCode(Normal::FAIL,"请求失败");
        }
        MicroServiceLog::instance()->addLog([
            'interface_code'    => self::account.'_'.$method,
            'request_url'       => $url,
            'request_param'     => json_encode($data),
            'response_code'     => $returnMsg['code'],
            'response_data'     => $res,
            'start_time'        => $start_time,
        ]);
        
        
        return $returnMsg;
    }
}